using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Runtime.InteropServices;
using System.Windows.Interop;
using System.Windows.Threading;
using NLarge.Properties;
using System.Diagnostics;
using System.Windows.Media.Animation;

namespace NLarge
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// Rob Burke
    /// robburke@microsoft.com
    /// Released under MS-PL (Microsoft permissive license):  http://www.microsoft.com/resources/sharedsource/licensingbasics/permissivelicense.mspx 
    /// Copyright (c) Microsoft Corporation.  All rights reserved.
    /// </summary>
    public partial class Window1 : System.Windows.Window
    {

        public Window1()
        {
            InitializeComponent();
        }
        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            AboutNLarge.KillAboutDialog();
            IconHolder.NI.Visible = false;
            base.OnClosing(e);
        }

        private NotifyIconHolder IconHolder;

        private void HandleFormLoad(object sender, RoutedEventArgs e)
        {
            CountdownTimer.Interval = TimeSpan.FromMilliseconds(250);
            CountdownTimer.Tick += new EventHandler(CountdownTimer_Tick);

            Settings.Default.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(SettingsPropertyChanged);

            System.Drawing.Point tempPnt = new System.Drawing.Point((int)ActiveScreenBounds.Width/2, (int)ActiveScreenBounds.Height/2);
            SpotGrabInstance = new SpotGrab(tempPnt, (int)ActiveScreenBounds.Width, (int)ActiveScreenBounds.Height);

            UserActivityHookInstance = new UserActivityHook();
            UserActivityHookInstance.OnMouseActivity += new System.Windows.Forms.MouseEventHandler(MouseMoveHandler);
            UserActivityHookInstance.KeyDown30 += new Key30EventHandler(GlobalKeyDown);
            UserActivityHookInstance.KeyUp += new System.Windows.Forms.KeyEventHandler(GlobalKeyUp);

            IntervalDispatcherTimer.Interval = TimeSpan.FromMilliseconds(50);
            IntervalDispatcherTimer.Tick += new EventHandler(HandleCustomAnimationTimer);

            this.Topmost = true;
            this.Focus();
            SketchInkCanvas.Focus();

            ZoomActive = false;
            IconHolder = new NotifyIconHolder(this);

            ReconfigureAllFromSettings();

            if (!Settings.Default.HasRunBefore)
            {
                AboutNLarge.PresentAboutDialog();
                Settings.Default.HasRunBefore = true;
                Settings.Default.Save();
            }

            App.Current.Deactivated += new EventHandler(Current_Deactivated);
            this.StateChanged += new EventHandler(Window1_StateChanged);
        }

        void Window1_StateChanged(object sender, EventArgs e)
        {
            if (WindowState == WindowState.Normal)
            {
                this.Top = ActiveScreenBounds.Top;
                this.Left = ActiveScreenBounds.Left;
                this.Width = ActiveScreenBounds.Width;
                this.Height = ActiveScreenBounds.Height;
                this.WindowState = WindowState.Maximized;
            }
        }


        void SettingsPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            ReconfigureAllFromSettings();
        }

        private void ReconfigureAllFromSettings()
        {
            // These aren't DependencyProperties.
            System.Drawing.Color color = Settings.Default.PenColor;
            InkDrawingAttributes.Color =
                System.Windows.Media.Color.FromArgb(color.A, color.R, color.G, color.B);
            InkDrawingAttributes.Width = Settings.Default.PenWidth;
            InkDrawingAttributes.Height = Settings.Default.PenWidth;
        }



        private DispatcherTimer IntervalDispatcherTimer = new DispatcherTimer();
        UserActivityHook UserActivityHookInstance;
        SpotGrab SpotGrabInstance;

        private Rect ActiveScreenBounds = new Rect(0, 0, 10, 10);

        private void CaptureScreenImage(System.Drawing.Point globalMousePosition)
        {
            System.Windows.Forms.Screen[] allScreens = System.Windows.Forms.Screen.AllScreens;
            System.Windows.Forms.Screen screenToUse = System.Windows.Forms.Screen.PrimaryScreen;
            foreach (System.Windows.Forms.Screen screen in allScreens)
            {
                if (screen.Bounds.Contains(globalMousePosition))
                {
                    screenToUse = screen;
                }
            }
            ActiveScreenBounds = new Rect(screenToUse.Bounds.Left, screenToUse.Bounds.Top,
                screenToUse.Bounds.Width, screenToUse.Bounds.Height);

            SpotGrabInstance.CursorPosition = new System.Drawing.Point(
                (int)(ActiveScreenBounds.X + ActiveScreenBounds.Width / 2),
                (int)(ActiveScreenBounds.Y + ActiveScreenBounds.Height / 2));
            SpotGrabInstance.SpotSize = new System.Drawing.Size((int)ActiveScreenBounds.Width, (int)ActiveScreenBounds.Height);
            SpotGrabInstance.Capture();
            ZoomImageBrush.Source = SpotGrabInstance.CurrentSpotBmpSrc;
        }

        private bool ZoomActiveRep;
        public bool ZoomActive
        {
            get { return ZoomActiveRep; }
            set 
            { 
                ZoomActiveRep = value;
                if (value)
                {
                    CaptureScreenImage(CurrentMousePosition);
                    IsTransformFrozen = false;
                    CurrentScale = 1;
                    DesiredScale = DesiredScaleCurrent;
                    CurrentTransformX = CurrentMousePosition.X - ActiveScreenBounds.X;
                    CurrentTransformY = CurrentMousePosition.Y - ActiveScreenBounds.Y;
                    GetDesiredTransformXYFromCurrentMousePosition();
                    ConstrainDesiredTransformXY();


                    SketchInkCanvas.Width = ActiveScreenBounds.Width;
                    SketchInkCanvas.Height = ActiveScreenBounds.Height;

                    this.Opacity = 0;


                    if (ActiveScreenBounds != LastActiveBoundsRect)
                    {
                        ResizeToActiveScreenAndMazimize();
                    }
                    else
                    {
                        this.Visibility = Visibility.Visible;
                    }

                    this.Topmost = true;
                    App.Current.MainWindow.Activate();
                    ZoomCanvas.Visibility = Visibility.Visible;
                    this.InvalidateVisual();
                    this.Focus();
                    SketchInkCanvas.Focus();
                    IntervalDispatcherTimer.Start();

                    DoubleAnimation a = new DoubleAnimation(0, 1, new Duration(TimeSpan.FromSeconds(0.4)), FillBehavior.HoldEnd);
                    a.Completed += delegate { this.Focus(); };
                    a.Freeze();
                    ZoomCanvas.BeginAnimation(Canvas.OpacityProperty, a);
                    //CountdownCanvas.BeginAnimation(Canvas.OpacityProperty, a); // bound to setting
                    this.BeginAnimation(Canvas.OpacityProperty, a);
                }
                else
                {
                    IntervalDispatcherTimer.Stop();
                    CountdownTimer.Stop();

                    DoubleAnimation a = new DoubleAnimation(1, 0, new Duration(TimeSpan.FromSeconds(0.2)), FillBehavior.HoldEnd);
                    a.Completed += delegate 
                    { 
                        this.Visibility = Visibility.Hidden;
                        CountdownCanvas.Visibility = Visibility.Hidden;
                        ZoomCanvas.Background = null;
                        TextCanvas.Children.Clear();
                    };
                    a.Freeze();
                    //ZoomCanvas.BeginAnimation(Canvas.OpacityProperty, a); // unnecessary
                    //CountdownCanvas.BeginAnimation(Canvas.OpacityProperty, a); // bound to setting
                    this.BeginAnimation(Canvas.OpacityProperty, a);
                }
            }
        }

        private void ResizeToActiveScreenAndMazimize()
        {
            Hide();
            Topmost = false;
            WindowState = WindowState.Normal;
            Show();

            this.Top = ActiveScreenBounds.Top;
            this.Left = ActiveScreenBounds.Left;
            this.Width = ActiveScreenBounds.Width;
            this.Height = ActiveScreenBounds.Height;

            Hide();
            WindowStyle = WindowStyle.None;
            WindowState = WindowState.Maximized;
            this.Topmost = true;
            Show();
            LastActiveBoundsRect = ActiveScreenBounds;
        }

        private Rect LastActiveBoundsRect = Rect.Empty;
        bool IsTransformFrozen = false;

        bool IsVanishingOnTimer = false;
        DateTime TimeVanishingStarted = DateTime.Now;


        private void HandleCustomAnimationTimer(object sender, EventArgs e)
        {
            double zoomRate = Settings.Default.ZoomSpeed;
            double zoomRateOnVanish = zoomRate * 0.9375;
            double panRate = 0.6;
            double currentZoomRate = IsVanishingOnTimer ? zoomRateOnVanish : zoomRate;

            if (!IsTransformFrozen)
            {
                CurrentScale = CurrentScale * currentZoomRate + DesiredScale * (1 - currentZoomRate);
                CurrentTransformX = CurrentTransformX * panRate + DesiredTransformX * (1 - panRate);
                CurrentTransformY = CurrentTransformY * panRate + DesiredTransformY * (1 - panRate);
            }
                
            UpdateTransform();

            if (IsVanishingOnTimer && (CurrentScale < 1.01 || DateTime.Now > TimeVanishingStarted.AddMilliseconds(2000)))
            {
                ZoomActive = false;
            }
        }

        private ModifierKeys CurrentModifierKeys = ModifierKeys.None;
        private Key CurrentKey = Key.None;

        public void GlobalKeyDown(object sender, Key30EventArgs e)
        {
            CurrentModifierKeys = e.ModifierKeys;
            CurrentKey = e.Key;

            if (Settings.Default.ZoomKey != Key.None
                && CurrentModifierKeys == Settings.Default.ZoomModifier
                && CurrentKey == Settings.Default.ZoomKey)
            {
                if (!(ZoomActive || AboutNLarge.IsDialogVisible))
                {
                    BeginNewZoomIn(true);
                    e.Handled = true;
                }
            }
            if (Settings.Default.DrawKey != Key.None
                && CurrentModifierKeys == Settings.Default.DrawModifier
                && CurrentKey == Settings.Default.DrawKey)
            {
                if (!(ZoomActive || AboutNLarge.IsDialogVisible))
                {
                    BeginNewZoomIn(false);
                    e.Handled = true;
                }
            }
            if (Settings.Default.TimerKey != Key.None
                && CurrentModifierKeys == Settings.Default.TimerModifier
                && CurrentKey == Settings.Default.TimerKey)
            {
                if (!(ZoomActive || AboutNLarge.IsDialogVisible))
                {
                    BeginCountdown();
                    e.Handled = true;
                }
            }
        }

        internal void BeginNewZoomIn(bool shouldZoomIn)
        {
            CountdownCanvas.Visibility = Visibility.Hidden;
            IsVanishingOnTimer = false;
            DesiredScaleCurrent = shouldZoomIn ? Settings.Default.ZoomDepth : 1;
            ZoomActive = true;
        }

        public void GlobalKeyUp(object sender, System.Windows.Forms.KeyEventArgs e)
        {
        }

        private void ClearAnnotations()
        {
            SketchInkCanvas.Strokes.Clear();
            TextCanvas.Children.Clear();
        }

        private void StartVanishingOnTimer()
        {
            ClearAnnotations();
            IsTransformFrozen = false;
            IsVanishingOnTimer = true;
            TimeVanishingStarted = DateTime.Now;
            DesiredScale = 1;

        }


        private System.Drawing.Point m_CurrentMousePosition;

        public System.Drawing.Point CurrentMousePosition
        {
            get { return m_CurrentMousePosition; }
            set { 
                m_CurrentMousePosition = value;

                if (!IsTransformFrozen && !IsVanishingOnTimer)
                {
                    GetDesiredTransformXYFromCurrentMousePosition();
                }

                ConstrainDesiredTransformXY();

                UpdateTransform();
            }
        }

        private void GetDesiredTransformXYFromCurrentMousePosition()
        {
            DesiredTransformX = CurrentMousePosition.X - ActiveScreenBounds.X;
            DesiredTransformY = CurrentMousePosition.Y - ActiveScreenBounds.Y;
            if (DesiredTransformY > ActiveScreenBounds.Height)
                DesiredTransformY = ActiveScreenBounds.Height;
            if (DesiredTransformX > ActiveScreenBounds.Width)
                DesiredTransformX = ActiveScreenBounds.Width;
            if (DesiredTransformY < 0) DesiredTransformY = 0;
            if (DesiredTransformX < 0) DesiredTransformX = 0;
        }

        private void ConstrainDesiredTransformXY()
        {
            double maxTransformXTarget, maxTransformYTarget;
            GetMaxTransformsForScale(out maxTransformXTarget, out maxTransformYTarget, DesiredScale);
            double transformX = (ActiveScreenBounds.Width / 2 - DesiredTransformX);
            double transformY = (ActiveScreenBounds.Height / 2 - DesiredTransformY);

            //this.DebugLabel.Content = "MaxXTarget " + maxTransformXTarget + " DesiredtransformX " + DesiredTransformX;


        }

        private void UpdateTransform()
        {

            TransformGroup tg = new TransformGroup();

            double transformX = (ActiveScreenBounds.Width / 2 - CurrentTransformX);
            double transformY = (ActiveScreenBounds.Height / 2 - CurrentTransformY);

            double maxTransformXCurrent, maxTransformYCurrent;
//            double maxTransformXTarget, maxTransformYTarget;
            GetMaxTransformsForScale(out maxTransformXCurrent, out maxTransformYCurrent, CurrentScale);
//            GetMaxTransformsForScale(out maxTransformXTarget, out maxTransformYTarget, DesiredScale);
            //double maxTransformX = maxTransformXTarget > 0
            //    ? Math.Min(maxTransformXCurrent, maxTransformXTarget)
            //    : Math.Max(maxTransformXCurrent, maxTransformXTarget);
            //double maxTransformY = maxTransformYTarget > 0
            //    ? Math.Min(maxTransformYCurrent, maxTransformYTarget)
            //    : Math.Max(maxTransformYCurrent, maxTransformYTarget);
            double maxTransformX = maxTransformXCurrent;
            double maxTransformY = maxTransformYCurrent;

            if (transformX > maxTransformX) transformX = maxTransformX;
            if (transformX < -maxTransformX) transformX = -maxTransformX;
            if (transformY > maxTransformY) transformY = maxTransformY;
            if (transformY < -maxTransformY) transformY = -maxTransformY;

            TranslateTransform tt = new TranslateTransform(
                transformX,
                transformY);

            ScaleTransform st = new ScaleTransform(CurrentScale, CurrentScale);

            tg.Children.Add(tt);
            tg.Children.Add(st);
            ZoomImageBrush.RenderTransform = tg;
            ZoomImageBrush.RenderTransformOrigin = new Point(0.5, 0.5);
            
        }

        private void GetMaxTransformsForScale(out double maxTransformX, out double maxTransformY, double scale)
        {
            maxTransformX = ActiveScreenBounds.Width * 0.5 * (CurrentScale - 1) / scale;
            maxTransformY = ActiveScreenBounds.Height * 0.5 * (CurrentScale - 1) / scale;
        }

        double CurrentScale = 1;

        private double m_DesiredScale = 1;
        public double DesiredScale
        {
            get { return m_DesiredScale; }
            set
            {
                m_DesiredScale = value;
                ConstrainDesiredTransformXY();
            }
        }
        double DesiredScaleCurrent = 2; 
        double CurrentTransformX = 0;
        double CurrentTransformY = 0;
        double DesiredTransformX = 0;
        double DesiredTransformY = 0;

        public void MouseMoveHandler(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            CurrentMousePosition = new System.Drawing.Point(e.X, e.Y);
            UpdateTransform();
        }

        public void HandleMouseDown(object sender, RoutedEventArgs e)
        {
            IsTransformFrozen = true;

            MouseButtonEventArgs me = e as MouseButtonEventArgs;
            if (me.RightButton == MouseButtonState.Pressed)
            {
                StartVanishingOnTimer();
            }
        }


        public void HandleMouseUp(object sender, RoutedEventArgs e)
        {
        }

        public void HandleMouseMove(object sender, RoutedEventArgs e)
        {
        }

        public void HandleMouseWheel(object sender, RoutedEventArgs e)
        {
            MouseWheelEventArgs me = e as MouseWheelEventArgs;
            if (me.Delta > 0)
            {
                if (CountdownCanvas.Visibility == Visibility.Visible)
                {
                    TargetTime = TargetTime.AddMinutes(1);
                    UpdateTimeLabel();
                }
                else
                {
                    ZoomIn();
                }
            }
            else
            {
                if (CountdownCanvas.Visibility == Visibility.Visible)
                {
                    TargetTime = TargetTime.AddMinutes(-1);
                    UpdateTimeLabel();
                }
                else
                {
                    ZoomOut();
                }
            }
        }

        public void HandleKeyDown(object sender, RoutedEventArgs e)
        {
            KeyEventArgs ke = e as KeyEventArgs;
            bool textboxHasFocus = false;
            foreach (TextBox tb in TextCanvas.Children)
            {
                if (tb.IsFocused) textboxHasFocus = true;
            }
            if (ke != null && !textboxHasFocus)
            {
                if (ke.Key == Key.Up || ke.Key == Key.Right || ke.Key == Key.OemPlus)
                {
                    if (CountdownCanvas.Visibility == Visibility.Visible)
                    {
                        TargetTime = TargetTime.AddMinutes(1);
                        UpdateTimeLabel();
                    }
                    else
                    {
                        ZoomIn();
                    }
                }
                if (ke.Key == Key.Down || ke.Key == Key.Left || ke.Key == Key.OemMinus)
                {
                    if (CountdownCanvas.Visibility == Visibility.Visible)
                    {
                        TargetTime = TargetTime.AddMinutes(-1);
                        UpdateTimeLabel();
                    }
                    else
                    {
                        ZoomOut();
                    }
                }
                if (ke.Key == Key.Escape)
                {
                    StartVanishingOnTimer();
                }
                if (ke.Key == Key.E)
                {
                    ClearAnnotations();
                }
                if (ke.Key == Key.C)
                {
                    ClearAnnotations();
                    if (IsTransformFrozen) IsTransformFrozen = false;
                }
                if (ke.Key == Key.R)
                {
                    Settings.Default.PenColor = System.Drawing.Color.FromArgb(210, 192, 0, 0);
                }
                if (ke.Key == Key.G)
                {
                    Settings.Default.PenColor = System.Drawing.Color.FromArgb(210, 76, 198, 34);
                }
                if (ke.Key == Key.B)
                {
                    Settings.Default.PenColor = System.Drawing.Color.FromArgb(210, 49, 68, 208);
                }
                if (ke.Key == Key.O)
                {
                    Settings.Default.PenColor = System.Drawing.Color.FromArgb(210, 196, 164, 11);
                }
                if (ke.Key == Key.Y)
                {
                    Settings.Default.PenColor = System.Drawing.Color.FromArgb(210, 255, 255, 0);
                }
                if (ke.Key == Key.W)
                {
                    ZoomImageBrush.Source = null;
                    ZoomCanvas.Background = new SolidColorBrush(Color.FromArgb(255, 255, 255, 255));
                }
                if (ke.Key == Key.T)
                {
                    IsTransformFrozen = true;
                    TextBox tb = new TextBox();
                    Point mousePosition = Mouse.GetPosition(TextCanvas);
                    Canvas.SetTop(tb, mousePosition.Y - 25);
                    Canvas.SetLeft(tb, mousePosition.X);
                    tb.FontSize = 36;
                    tb.Background = new SolidColorBrush(Color.FromArgb(128, 0, 0, 0));
                    tb.Foreground = new SolidColorBrush(Color.FromArgb(225, 255, 255, 255));
                    tb.Height = 60;
                    TextCanvas.Children.Add(tb);
                    tb.Focus();
                    tb.Text = "";
                    e.Handled = true;
                }
            }
        }

        void Current_Deactivated(object sender, EventArgs e)
        {
            if (ZoomActive && CountdownCanvas.Visibility == Visibility.Hidden
                && WindowState == WindowState.Maximized )
            {
                StartVanishingOnTimer();
            }
        }

        public void ZoomOut()
        {
            DesiredScale *= 0.75;
            if (DesiredScale < 1) DesiredScale = 1;
        }

        public void ZoomIn()
        {
            DesiredScale /= 0.75;
            if (DesiredScale > 8) DesiredScale = 8;
        }



        internal void BeginCountdown()
        {
            BeginNewZoomIn(false);
            this.Topmost = false;
            TargetTime = DateTime.Now.Add(Settings.Default.BreakTimeSpan);
            CountdownCanvas.Visibility = Visibility.Visible;
            UpdateTimeLabel();

            if (!CountdownTimer.IsEnabled)
            {
                CountdownTimer.Start();
            }
        }
        private DateTime TargetTime = DateTime.Now;
        void CountdownTimer_Tick(object sender, EventArgs e)
        {
            UpdateTimeLabel();
        }

        private void UpdateTimeLabel()
        {
            TimeSpan timeSpan = TargetTime - DateTime.Now;
            if (timeSpan < TimeSpan.Zero)
            {
                CountdownLabel.Foreground = new SolidColorBrush(Color.FromArgb(255, 255, 128, 128));
                timeSpan = -timeSpan;
            }
            else
            {
                CountdownLabel.Foreground = new SolidColorBrush(Color.FromArgb(255, 255, 255, 255));
            }
            DateTime dt = DateTime.MinValue.Add(timeSpan);
            string s = dt.ToString("HH:mm:ss");
            CountdownLabel.Content = s;
        }
        DispatcherTimer CountdownTimer = new DispatcherTimer();
    }
}